import processing.core.PApplet;
import toxi.geom.Vec3D;

import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.*;

import oscP5.*;
import netP5.*;

import netP5.NetAddressList;
import TUIO.TuioCursor;

/**
 * A Page is an object that manages the Global interaction.  It contains the Curves, the text,
 * and the tObjects that make the interaction possible.  There could be a number of potential pages
 * in a session for managing several tasks that are grouped together into a project.  The way that
 * individual tasks are executed and manifest into tangible results on the page, are by pushing
 * user input through user configurations.  The responsibilities of a Page:
 * 
 *  -serving as a container for text, tobjects, and curves.
 *  -maintaining a list of member configurations
 *    -uses the list of configurations to interpret how user's input is manifested on the page
 * 
 */
public class Page {
  private PApplet parent;
  private String pageID;
  private int broadcastPort = 45678;
  private ArrayList tObjects = new ArrayList(1000);
  private ArrayList curves = new ArrayList(1000); 
  private ArrayList configurations = new ArrayList(100);
  private String displayString; //this is literally the string which holds all the text on the page
  private static final int DRAWMODE = 0;
  private static final int TEXTMODE = 1;
  private static final int ACTIONSMODE = 2;
  
  public Page (PApplet p) {
    
    //a page starts off with nothing in it
    parent = p;
    displayString = "";
    
  }
  
  public Page (PApplet p, String encodedPage) {
    parent = p;
    String[] pageParts = parent.split(encodedPage, '[');
    
    for(int i = 0; i < 4; i++) {
      pageParts[i] = pageParts[i].trim();
    }

    if (!pageParts[0].equalsIgnoreCase("/page")) {
      parent.println("This file is malformatted.  Page cannot be loaded");
      displayString = "";
    } else {
      
      pageID = pageParts[0].substring(5).trim();

      //parse configs
      PApplet.println(pageParts[1]);
      String[] configs = parent.split(pageParts[1],']');
      for(int i = 1; i < configs.length; i++) {
        configs[i] = configs[i].trim();
        addConfiguration(new Configuration(parent,configs[i]));
      }
      
      //parse curves
      PApplet.println(pageParts[2]);
      String[] cvs = parent.split(pageParts[2],']');
      for(int i = 1; i < cvs.length; i++) {
        cvs[i] = cvs[i].trim();
        Curve c = new Curve(parent,cvs[i]);
        addCurve(c);
      }
      
      //parse objects
      PApplet.println(pageParts[3]);
      String[] objs = parent.split(pageParts[3],']');
      for(int i = 0; i < objs.length - 1; i++) {
        objs[i] = objs[i].trim();
        addObject(new tObject(parent,objs[i]));
      }
      
      //set displayString
      displayString = pageParts[4];
    }
    
  }
  
  public void exist() {
    
    parent.background(255);
    
    for (int i = 0; i < tObjects.size(); i++) {
          tObject e = (tObject)tObjects.get(i);
          e.draw(); 
    }
    
    for (int i = 0; i < curves.size(); i++) {
          Curve e = (Curve)curves.get(i);
          e.draw(); 
    }
    
    parent.text(displayString,2,2,parent.width-2,parent.height-2);
    
  }
  
  public void setPageID(String _pageID) {
    pageID = _pageID;
  }
  
  public String getID () {
    return pageID;
  }
  
  
  
  
  
  
  
  
  /** Cursor Events **/
  
  public void curAdd(String configIP, TuioCursor tcur) {
    Configuration cfn = getConfigurationByIP(configIP);
    parent.println("cfn is " + cfn.toString());
    float tmpX = tcur.getScreenX(parent.width) / parent.width;
    float tmpY = tcur.getScreenY(parent.height) / parent.height;
    //int cur = (new Long(tcur.getSessionID())).intValue();
    int cur = tcur.getCursorID();
    
    
    switch(cfn.getMode()) {
      case(DRAWMODE): //drawmode
        //do nothing, press determines the addition of a curve
        break;
      case(TEXTMODE): //textmode
        //do nothing, press determines cursor placement
        break;
      case(ACTIONSMODE):
        //do nothing, press determines object selection
        break;
      default:
        break;
    }
  }
  
  public void curPress(String configIP, TuioCursor tcur) {
    Configuration cfn = getConfigurationByIP(configIP);
    float tmpX = tcur.getScreenX(parent.width);
    float tmpY = tcur.getScreenY(parent.height);
    //int cur = (new Long(tcur.getSessionID())).intValue();
    int cur = tcur.getCursorID();
    
    switch(cfn.getMode()) {
      case(DRAWMODE): //drawmode
        addCurve(new Vec3D(tmpX,tmpY,0),cur);
        break;
      case(TEXTMODE):
        //press determines cursor placement within text string
        break;
      case(ACTIONSMODE):
        //press determines object selection
        break;
      default:
        break;
    }
  }
  
  public void curDrag(String configIP, TuioCursor tcur) {
    Configuration cfn = getConfigurationByIP(configIP);
    float tmpX = tcur.getScreenX(parent.width);
    float tmpY = tcur.getScreenY(parent.height);
    //int cur = (new Long(tcur.getSessionID())).intValue();
    int cur = tcur.getCursorID();
    
    switch(cfn.getMode()) {
      case(DRAWMODE): //drawmode
        getCurve(cur).push(new Vec3D(tmpX,tmpY,0), cfn.getDrawWidth());
        break;
      case(TEXTMODE):
        //press determines cursor placement within text string
        break;
      case(ACTIONSMODE):
        //press determines object selection
        break;
      default:
        break;
    }
  }
  
  public void curRelease(String configIP, TuioCursor tcur) {
    Configuration cfn = getConfigurationByIP(configIP);
    float tmpX = tcur.getScreenX(parent.width);
    float tmpY = tcur.getScreenY(parent.height);
    //int cur = (new Long(tcur.getSessionID())).intValue();
    int cur = tcur.getCursorID();
    
    switch(cfn.getMode()) {
      case(DRAWMODE): //drawmode
        getCurve(cur).setFinished();
        break;
      case(TEXTMODE):
        //press determines cursor placement within text string
        break;
      case(ACTIONSMODE):
        //press determines object selection
        break;
      default:
        break;
    }
  }
  
  
  
  
  
  /** KeyPRess Events **/
  
  public void keyPress( String configIP, char key) {
   Configuration cfn = getConfigurationByIP(configIP);
   PApplet.println("Parsing key from configuration: " + cfn.getIP()); 
    
      
   //drawing 
   if (cfn.getMode() == 0) {
      if( key == 'z' || key == 'Z' ) {
        cfn.setDrawWidth(cfn.getDrawWidth() - 5);
      }
      
      if( key == 'x' || key == 'X' ) {
        cfn.setDrawWidth(cfn.getDrawWidth() + 5);
      }
      
      if( key == 'c' || key == 'C' ) {
        cfn.setDrawType(0); //calligraphy
      }
      
      if( key == 'w' || key == 'W' ) {
        cfn.setDrawType(1); //worm
      }
      
   }
   
   //text
   if (cfn.getMode() == 1) {
     PApplet.println("In page. Go!");
     if (key == 8) {
       //this is delete.  remove a character from the string.
       String pageContent = getDisplayString();
       if (pageContent.length() > 0) {
         setDisplayString(pageContent.substring(0, pageContent.length() - 1));
       } //else do nothing
       
     } else if (key == 16) {
       //this is shift.  do nothing.
     } else {
       appendDisplayString("" + key); 
     }
      
   }
   
   //actions
   if (cfn.getMode() == 2) {
     
     if( key == 's' || key == 'S' ) {
       try {
          PrintWriter out = parent.createWriter("/data/page_" + parent.day() + "." + parent.month() + "." + parent.year() + "." + parent.minute() + ".pge");
          out.println(toString());
          out.close();
          PApplet.println("Saving page as " + "page_" + parent.day() + "." + parent.month() + "." + parent.year() + "." + parent.minute() + ".pge");
       } catch (Exception e){
          e.printStackTrace();
       }
     }
   }
   
  }
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  /** Handles for the Drawing Mode **/
  
  private void addCurve(Vec3D v, int cur) {
     curves.add(new Curve(parent,v,cur));
  }
  
  private void addCurve(Curve _curve) {
     curves.add(_curve);
  }
  
  private void finishCurve(int cur) {
     Curve v = getCurve(cur);
     v.setFinished();
  }
  
  private Curve getCurve (int cursorID) {
    Curve tmpCurve = null;
    for (int i = numCurves()-1; i >= 0; i--){
      tmpCurve = ((Curve)curves.get(i));
      if (tmpCurve.getCursor() == cursorID) {
        return tmpCurve;
      }
    }
    return tmpCurve;
  }
  
  private ArrayList getCurves() {
    return curves;
  }
  
  private int numCurves() {
    return curves.size();
  }
  
  
  
  
  
  
  /** Text Mode **/
  
  public String getDisplayString() {
    return displayString;
  }
  
  public void setDisplayString(String s) {
    displayString = s;
  }
  
  public void appendDisplayString(String s) {
    displayString += s;
  }
  
  
  

  
  /** Actions Mode **/
  
  public ArrayList getObjects() {
    return tObjects;
  }
  
  public void addObject(tObject obj) {
    PApplet.println("In addObject");
    tObjects.add(obj);
  }
  
  
  
  
  
  /** Configurations Control **/
  
  public void addConfiguration(Configuration c) {
    configurations.add(c);
    c.setPage(this);
  }
  
  //The order corresponds to the host's user list
  public ArrayList getConfigurations() {
    return configurations;
  }
  
  public Configuration getConfigurationByIP(String _ip) {
        
    for (int i = 0; i < configurations.size(); i++) {
      Configuration tmp = ( (Configuration)configurations.get(i) );
      if( tmp.getIP().equalsIgnoreCase(_ip) ) {
        return tmp;
      }
    }

    PApplet.println("There was an error retrieving your configuration.");
    return null;
  }
  
  public NetAddressList getNetAddresses() {
    NetAddressList tmp = new NetAddressList();
    for(int i = 0; i < configurations.size(); i++) {
      tmp.add(new NetAddress(((Configuration)configurations.get(i)).getIP(),broadcastPort));
    }
    return tmp;
  }
  
  public void changeConfiguration(String _configIP, int _mode, int _dwidth, int _dtip, int _dcol, int _tsize, int _tcol) {
    Configuration cfg = getConfigurationByIP(_configIP);
    cfg.setMode(_mode);
    cfg.setDrawWidth(_dwidth);
    cfg.setDrawType(_dtip);
    cfg.setDrawColor(_dcol);
    cfg.setTextSize(_tsize);
    cfg.setTextColor(_tcol);
  }

  public void removeConfiguration(String _ip) {
    configurations.remove(getConfigurationByIP(_ip));
    
  }
  
  
  
  
  /** This function simply encodes the page as a string for session saving and loading, allowing for
   *  users to enter the session in the middle and getting the most updated page, and for eventually
   *  serving as the basis for a peer to peer "site".  
   */
  public String toString() {
     String thispage = null;
     
     thispage = "/page " + pageID + " [";
     
     ArrayList tmpConfig = getConfigurations();
     for(int i = 0; i < tmpConfig.size(); i++) {
       thispage = thispage + " ] " + tmpConfig.get(i).toString();
     }
     
     thispage = thispage + "[";
     
     ArrayList tmpCurves = getCurves();
     for(int i = 0; i < tmpCurves.size(); i++) {
       thispage = thispage + " ] " + tmpCurves.get(i).toString();
     }
     
     thispage = thispage + "[";
     
     ArrayList tmpObjects = getObjects();
     for(int i = 0; i < tmpObjects.size(); i++) {
       thispage = thispage + " ] " + tmpObjects.get(i).toString();
     }
     
     thispage = thispage + "[" + displayString;
     
     return thispage;
  }
  
}
